package com.example.mediademo.decode;

import android.media.MediaCodec;
import android.media.MediaFormat;
import android.util.Log;

import com.example.mediademo.Configs;
import com.example.mediademo.decode.interfaces.IDecodeStateListener;
import com.example.mediademo.decode.interfaces.IDecoder;
import com.example.mediademo.decode.interfaces.IExtractor;

import java.io.File;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author baj
 */
public abstract class BaseDecoder implements IDecoder {

    public BaseDecoder(String filePath) {
        this.mFilePath = filePath;
    }

    // region 基础参数定义

    private static final String TAG = "baiaj";

    /**
     * 解码器是否在运行
     */
    private boolean mIsRunning = true;

    /**
     * 线程等待锁
     */
    private Object mLock = new Object();

    /**
     * 是否可以进入解码
     */
    private boolean mReadyForDecode;

    // endregion

    // region 解码相关参数

    protected String mFilePath;

    protected MediaCodec mCodec;

    /**
     * 音视频提取器
     */
    protected IExtractor mExtractor;

    /**
     * 解码输入缓存区
     */
    protected List<ByteBuffer> mInputBuffers = new ArrayList<>();

    /**
     * 解码输出缓存区
     */
    protected List<ByteBuffer> mOutPutBuffers = new ArrayList<>();

    /**
     * 解码数据信息
     */
    private MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();

    /**
     * 解码状态
     */
    private DecodeState mState;

    /**
     * 解码状态监听器
     */
    protected IDecodeStateListener mStateListener;

    /**
     * 流数据是否结束
     */
    private boolean mIsEos;

    protected int mVideoWidth = 0;

    protected int mVideoHeight = 0;

    private long mDuration = 0;

    private long mStartPos = 0;

    private long mEndPos = 0;

    /**
     * 开始解码时间，用于音视频同步
     */
    private long mStartTimeForSync = -1L;

    /**
     * 是否需要音视频渲染同步
     */
    private boolean mSyncRender = true;


    // endregion

    @Override
    public void pause() {
        mState = DecodeState.PAUSE;
    }

    @Override
    public long seekTo(long pos) {
        return 0;
    }

    @Override
    public long seekAndPlay(long pos) {
        return 0;
    }

    @Override
    public void resume() {
        mState = DecodeState.DECODING;
        notifyDecode();
    }

    @Override
    public void stop() {
        mState = DecodeState.STOP;
        mIsRunning = false;
        notifyDecode();
    }

    @Override
    public boolean isDecoding() {
        return mState == DecodeState.DECODING;
    }

    @Override
    public boolean isSeeking() {
        return mState == DecodeState.SEEKING;
    }

    @Override
    public boolean isStop() {
        return mState == DecodeState.STOP;
    }

    @Override
    public void setStateListener(IDecodeStateListener listener) {
        mStateListener = listener;
    }


    @Override
    public int getWidth() {
        return mVideoWidth;
    }

    @Override
    public int getHeight() {
        return mVideoHeight;
    }

    @Override
    public long getDuration() {
        return mDuration;
    }

    @Override
    public int getRotationAngle() {
        return 0;
    }

    @Override
    public MediaFormat getMediaFormat() {
        if (mExtractor == null) {
            return null;
        }
        return mExtractor.getFormat();
    }

    @Override
    public int getTrack() {
        return 0;
    }

    @Override
    public String getFilePath() {
        return mFilePath;
    }

    @Override
    public IDecoder withoutSync() {
        mSyncRender = false;
        return this;
    }

    /**
     * 实际解码流程
     */
    @Override
    public void run() {
        try {
            // prepare 设置状态和回调
            if (mState == DecodeState.STOP) {
                mState = DecodeState.START;
            }
            if (mStateListener != null) {
                mStateListener.decodePrepare(this);
            }
            // 解码步骤：1.初始化,并启动解码器
            if (!init()) {
                return;
            }
            Log.i(TAG, "开始解码：" + mBufferInfo);
            while (mIsRunning) {
                // PAUSE STOP时会进入等待
                if (mState != DecodeState.START &&
                        mState != DecodeState.DECODING &&
                        mState != DecodeState.SEEKING) {
                    Log.i(TAG, "进入等待：" + mState);
                    waitDecode();
                    // ---------【同步时间矫正】-------------
                    //恢复同步的起始时间，即去除等待流失的时间
                    mStartTimeForSync = System.currentTimeMillis() - getCurTimeStamp();
                }
                // 如果STOP或者没有Running了，跳出循坏
                if (!mIsRunning || mState == DecodeState.STOP) {
                    mIsRunning = false;
                    break;
                }
                // 初始状态
                if (mStartTimeForSync == -1L) {
                    mStartTimeForSync = System.currentTimeMillis();
                }
                // 2.如果数据没解码完毕，将数据推入解码器解码(初始状态时mIsEos是false)
                if (!mIsEos) {
                    Log.i(TAG, "将数据推入解码器解码 mInputBuffers：" + mInputBuffers.size());
                    mIsEos = pushBufferToDecoder();
                }
                //【解码步骤：3. 将解码好的数据从缓冲区拉取出来】
                int index = pullBufferFromDecoder();
                Log.i(TAG, "将解码好的数据从缓冲区拉取出来 index：" + index);
                if (index >= 0) {
                    // 音视频同步
                    Log.i(TAG, "将音视频同步 ：mSyncRender" + mSyncRender);
                    if (mSyncRender && mState == DecodeState.DECODING) {
                        sleepRender();
                    }
                    //【解码步骤：4. 渲染】
                    if (mSyncRender) {
                        Log.i(TAG, "渲染 mOutPutBuffers " + mOutPutBuffers.size());
                        // 如果只是用于编码合成新视频，无需渲染
                        render(mOutPutBuffers.get(index),mBufferInfo);
                    }
                    //将解码数据传递出去
                    Frame frame = new Frame();
                    frame.buffer = mOutPutBuffers.get(index);
                    frame.setBufferInfo(mBufferInfo);
                    if (mStateListener != null) {
                        mStateListener.decodeOneFrame(this,frame);
                    }
                    //【解码步骤：5. 释放输出缓冲】
                    if (mCodec != null) {
                        Log.i(TAG, "释放输出缓冲 index:" + index + "mState:" + mState);
                        // 第二个参数，是个boolean，命名为render，这个参数在视频解码时，用于决定是否要将这一帧数据显示出来。
                        mCodec.releaseOutputBuffer(index,true);
                    }
                    if (mState == DecodeState.START) {
                        mState = DecodeState.PAUSE;
                    }
                    //【解码步骤：6. 判断解码是否完成】
                    if (mBufferInfo.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
                        Log.i(TAG, "解码结束");
                        mState = DecodeState.FINISH;
                        if (mStateListener != null) {
                            mStateListener.decodeFinish(this);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            Log.e(TAG,"解码失败:" + e.toString());
        } finally {
            doneDecode();
            release();
        }
    }

    private boolean init() {
        // 检查参数完整性
        if (mFilePath.isEmpty() || !new File(mFilePath).exists()) {
            Log.w(TAG, "文件路径为空");
            mStateListener.decodeError(this, "文件路径为空");
            return false;
        }
        if (!check()) {
            return false;
        }
        // 初始化数据提取器
        mExtractor = initExtractor();
        if (mExtractor == null || mExtractor.getFormat() == null) {
            Log.w(TAG, "Extractor异常,无法解析文件：" + mExtractor);
            return false;
        }
        // 初始化参数
        if (!initParams()) {
            return false;
        }
        // 初始化渲染器（视频不需要，音频是AudioTracker）
        if (!initRender()) {
            return false;
        }
        // 初始化解码器
        if (!initCodec()) {
            return false;
        }
        return true;
    }

    private void release() {
        try {
            Log.i(TAG, "解码停止，释放解码器");
            mState = DecodeState.STOP;
            mIsEos = false;
            if (mExtractor != null) {
                mExtractor.stop();
            }
            if (mCodec != null) {
                mCodec.stop();
                mCodec.release();
            }
            if (mStateListener != null) {
                mStateListener.decodeDestroy(this);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 初始化参数 提取一些必须的参数 如duration  width  height等
     *
     * @return
     */
    private boolean initParams() {
        try {
            MediaFormat format = mExtractor.getFormat();
            mDuration = format.getLong(MediaFormat.KEY_DURATION) / 1000;
            if (mEndPos == 0L) {
                mEndPos = mDuration;
            }
            initSpecParams(mExtractor.getFormat());
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    private boolean initCodec() {
        try {
            // 根据音视频编码格式初始化编解码器，并获取编码类型
            // video/avc，即H264；audio/mp4a-latm，即AAC  video/hevc h265
            String type = mExtractor.getFormat().getString(MediaFormat.KEY_MIME);
            mCodec = MediaCodec.createDecoderByType(type);
            // 配置解码器
            if (mCodec != null) {
                if (mExtractor.getFormat() != null &&
                        !(configCodec(mCodec, mExtractor.getFormat()))) {
                    waitDecode();
                }
                // 启动解码器
                mCodec.start();
                // 获取解码器缓冲区
                mInputBuffers = Arrays.asList(mCodec.getInputBuffers());
                mOutPutBuffers = Arrays.asList(mCodec.getOutputBuffers());
            }
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 解码线程进入等待
     */
    private void waitDecode() {
        try {
            if (mState == DecodeState.PAUSE && mStateListener != null) {
                mStateListener.decodePause(this);
            }
            synchronized (mLock) {
                mLock.wait();
            }
        } catch (Exception e) {
            Log.e(TAG, "waitDecode error:" + e.toString());
        }
    }

    /**
     * 通知解码线程继续运行
     */
    protected void notifyDecode() {
        synchronized (mLock) {
            Log.d(Configs.TAG,"唤醒线程");
            mLock.notifyAll();
        }
        if (mState == DecodeState.DECODING) {
            if (mStateListener != null) {
                mStateListener.decodeRunning(this);
            }
        }
    }

    private void sleepRender() throws InterruptedException {
        long passTime = System.currentTimeMillis() - mStartTimeForSync;
        long curTime = getCurTimeStamp();
        if (curTime > passTime) {
            Thread.sleep(curTime - passTime);
        }
    }

    private long getCurTimeStamp() {
        return mBufferInfo.presentationTimeUs / 1000;
    }

    /**
     * 将数据压入解码器输入缓冲区
     *
     * @return
     */
    private boolean pushBufferToDecoder() {
        boolean isEndOfStream = false;
        if (mCodec != null && mExtractor != null) {
            // 查询是否有可用的输入缓冲，返回缓冲索引,1000代表等待1000us，如果填入-1则无限等待
            int inputBufferIndex = mCodec.dequeueInputBuffer(1000);
            if (inputBufferIndex >= 0 && !mInputBuffers.isEmpty()) {
                // 通过缓冲索引 inputBufferIndex 获取可用的缓冲区，并使用Extractor提取待解码数据，填充到缓冲区中
                ByteBuffer inputBuffer = mInputBuffers.get(inputBufferIndex);
                int sampleSize = mExtractor.readBuffer(inputBuffer);
                if (sampleSize < 0) {
                    // 如果数据已经取完,压入数据结束标志：MediaCodec.BUFFER_FLAG_END_OF_STREAM
                    mCodec.queueInputBuffer(inputBufferIndex, 0, 0, 0,
                            MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                    isEndOfStream = true;
                } else {
                    mCodec.queueInputBuffer(inputBufferIndex, 0, sampleSize,
                            mExtractor.getCurrentTimeStamp(), 0);
                }
            }
        }
        return isEndOfStream;
    }

    private int pullBufferFromDecoder() {
        if (mCodec != null) {
            // 调用dequeueOutputBuffer方法查询是否有解码完成的可用数据，其中mBufferInfo用于获取数据帧信息，第二参数是等待时间，这里等待1000us，填入-1是无限等待
            int index = mCodec.dequeueOutputBuffer(mBufferInfo, 1000);
            // 输出缓冲区发生变化
            if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                mOutPutBuffers = Arrays.asList(mCodec.getOutputBuffers());
            } else {
                return index;
            }
        }
        return -1;
    }

    /**
     * 初始化子类自己特有的参数
     *
     * @param format MediaFormat
     */
    protected abstract void initSpecParams(MediaFormat format);

    /**
     * 初始化数据提取器
     */
    protected abstract IExtractor initExtractor();

    /**
     * 检查子类参数
     */
    protected abstract boolean check();

    /**
     * 初始化渲染器
     */
    protected abstract boolean initRender();

    /**
     * 配置解码器
     *
     * @param mCodec MediaCodec
     * @param format MediaFormat
     * @return
     */
    protected abstract boolean configCodec(MediaCodec mCodec, MediaFormat format);

    /**
     * 渲染
     */
    protected abstract void render(ByteBuffer outputBuffer,
                                   MediaCodec.BufferInfo bufferInfo);

    /**
     * 结束解码
     */
    protected abstract void doneDecode();

}
